package shop;

import java.util.*;
import java.io.*;
import java.util.Map.Entry;

import date.Date;
import shop.category.Category;
import shop.seller.Seller;

public class Shop {

	private static final String PATH_OF_SELLER = "../my-db/src/main/resources/seller";
	private static final String PATH_OF_CATEGORY = "../my-db/src/main/resources/products";
	private static final String ALL_PRODUCT = "all";
	private final int TOP_OF_PRODUCT = 10;
	private final boolean SELLER = true;
	private final boolean PRODUCT = false;
	private String nameProduct = null;
	private List<String> listOfNameProducts = null;

	public Shop() {

		listOfNameProducts = new ArrayList<String>();

		System.out.println("The following sellers work in the shop:");

		Seller seller = new Seller(new File(PATH_OF_SELLER));
		printList(seller.getMapOfSeller(), SELLER);

		System.out.println("\nThe following products exist in the shop:");

		Category cat = new Category(new File(PATH_OF_CATEGORY));
		getProductWithCategory(cat.getMapOfCategory());

		System.out.println("");
	}

	public void parseDates(String[] firstDate, String[] secondDate, String nameProduct,
			Map<String, List<Map<String, String>>> map) {

		List<Map<String, String>> listOfMap = new ArrayList<Map<String, String>>();
		String[] dateOfDB;

		this.nameProduct = nameProduct;

		if (verifyDate(firstDate) && verifyDate(secondDate)) {
			
			for (Map.Entry<String, List<Map<String, String>>> e : map.entrySet()) {
				dateOfDB = getDateOfDB(e);

				for (int i = 1; i < dateOfDB.length; i++)
					if (verifyRangeOfDate(firstDate[i], secondDate[i],dateOfDB[i])) {
						if (i + 1 == dateOfDB.length) {
							listOfMap.addAll(e.getValue());
						}
					} else
						break;
			}

			printProduct(listOfMap);
			printTop(countPrice(listOfMap, PRODUCT));
			printTheBestSeller(countPrice(listOfMap, SELLER));
		}
	}

	private void getProductWithCategory(
			Map<String, Map<Integer, Map<String, String>>> map) {

		for (Map.Entry<String, Map<Integer, Map<String, String>>> m : map.entrySet()) {
			System.out.println(m.getKey() + ":");
			printList(m.getValue(), PRODUCT);
		}
	}

	private void printList(Map<Integer, Map<String, String>> map,
			boolean ifSeller) {

		for (Map.Entry<Integer, Map<String, String>> m : map.entrySet()) {
			if (ifSeller)
				System.out.println(m.getKey() + 1 + ": " + getFullName(m.getValue()));
			else {
				System.out.println("Product: " + m.getValue().get("NAME")
						+ ", Price: " + m.getValue().get("PRICE"));
				listOfNameProducts.add(m.getValue().get("NAME"));
			}
		}
	}

	private void printProduct(List<Map<String, String>> list) {

		for (int i = 1; i <= list.size() - 1; i += 2)
			if (verifyProduct()) {
				if (list.get(i).get("NAME").equals(nameProduct)|| nameProduct.equals(ALL_PRODUCT))
					System.out.println(list.get(i).get("NAME") + " product was sold by "
							+ getFullName(list.get(i - 1)));
			} else {
				System.out.println(nameProduct + " product doesn't exit in product list of the shop");
				break;
			}
	}

	private void printTop(Map<String, Integer> map) {

		int count = 0;
		System.out.println("TOP" + TOP_OF_PRODUCT + ":");

		for (Map.Entry<String, Integer> e : map.entrySet())
			if (count < TOP_OF_PRODUCT) {
				count++;
				System.out.println(count + ": " + e.getKey() + " " + e.getValue());
			} else
				break;
	}

	private void printTheBestSeller(Map<String, Integer> map) {

		int value = 0;
		for (Map.Entry<String, Integer> e : map.entrySet()) {
			if (value > e.getValue())
				break;

			System.out.println("The best seller is " + e.getKey()
					+ ". Ammount of the sold productes: " + e.getValue() + "$");
			value = e.getValue();
		}

	}

	//Select from list map of seller and product. 
	//-Count of seller is started with 0 and then 2,4 etc.  
	//-Count of product is started with 1 and then 3,5 etc.
	private Map<String, Integer> countPrice(List<Map<String, String>> list,
			boolean ifSeller) {

		Map<String, Integer> map = new HashMap<String, Integer>();

		for (int i = 1; i <= list.size() - 1; i += 2) {
			String name = null;
			if (!ifSeller)
				name = list.get(i).get("NAME");
			else
				name = getFullName(list.get(i - 1));

			int price = parseToInt(list.get(i).get("PRICE"));
			if (map.get(name) == null)
				map.put(name, price);
			else {
				price += map.get(name);
				map.put(name, price);
			}
			map = sortByValues(map);
		}

		return map;
	}

	/*
	 * Java method to sort Map in Java by value e.g. HashMap or HashTable throw
	 * NullPointerException if Map contains null values It also sort values even
	 * if they are duplicates
	 */
	private <K extends Comparable, V extends Comparable> Map<K, V> sortByValues(
			Map<K, V> map) {

		List<Map.Entry<K, V>> entries = new LinkedList<Map.Entry<K, V>>(
				map.entrySet());

		Collections.sort(entries, new Comparator<Map.Entry<K, V>>() {

			public int compare(Entry<K, V> o1, Entry<K, V> o2) {
				return o2.getValue().compareTo(o1.getValue());
			}
		});

		// LinkedHashMap will keep the keys in the order they are inserted
		// which is currently sorted on natural ordering
		Map<K, V> sortedMap = new LinkedHashMap<K, V>();

		for (Map.Entry<K, V> entry : entries) {
			sortedMap.put(entry.getKey(), entry.getValue());
		}

		return sortedMap;
	}

	//This method perform concatenation of strings
	private String getFullName(Map<String, String> map) {

		return map.get("NAME") + " " + map.get("SURNAME");
	}

	private boolean verifyRangeOfDate(String firstDate, String secondDate, String dateOfDB) {

		if (parseToInt(firstDate) <= parseToInt(dateOfDB)
				&& parseToInt(secondDate) >= parseToInt(dateOfDB))
			return true;

		return false;
	}
	
	//Prices are parsed to integer 
	private int parseToInt(String value) {

		return Integer.parseInt(value);
	}

	private boolean verifyDate(String[] splitedDate) {

		Date d = new Date();

		if (d.verifyYear(splitedDate[0]) && d.verifyMonth(splitedDate[1])
				&& d.verifyDay(splitedDate[2]))
			return true;

		return false;
	}

	//Product is verified with the product list of the shop
	private boolean verifyProduct() {

		for (String name : listOfNameProducts)
			if (name.equals(nameProduct) || nameProduct.equals(ALL_PRODUCT))
				return true;

		return false;
	}
	
	//Key of the map is a date. The date is split to year, month and day    
	private String[] getDateOfDB(Map.Entry<String, List<Map<String, String>>> e) {

		return e.getKey().split("/");
	}

}